/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package JET.mygui;

import JET.mygui.interfaces.*;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;

/**
 *
 * @author raos
 */
public class MyGuiManager implements MouseListener, MouseMotionListener{

    public enum Mode {
        None,
        PrepareForDragTab,
        DragObj;
    }

    public  final static int    distanceToDrag = 35; // in pixels

    private static MyGuiManager instance;

    private ArrayList<MyGuiDnDReady> dnds;

    private Mode                mode = Mode.None;
    private GlassPane           glass;


    // Fields used for dragging component from one tabsPane to another
    public  MyGuiDnDReady       srcDnd;
    public  Object              objToDrag;
    public  Component           objParent;
    public  int                 index;
    public  Point               pickUpScrPoint;
    public  Container           rootContainer;

    private MyGuiManager() {
        dnds= new ArrayList<MyGuiDnDReady>();
    }

    public static MyGuiManager getInstance() {
        if (instance==null)
            instance = new MyGuiManager();

        return instance;
    }

    public void setRootContainer(Container c) {
        rootContainer = c;
    }

    public MultiSplitPane getRootMSP() {
        if (rootContainer==null)
            return null;

        for(int i=0; i<rootContainer.getComponentCount(); i++)
            if (rootContainer.getComponent(i) instanceof MultiSplitPane)
                return (MultiSplitPane) rootContainer.getComponent(i);
        
        return null;
    }

    protected void setMode(Mode mode) {
        this.mode = mode;
    }

    /**
     * Registers new tabs pane, as an object of interest and management for
     * gui manager.
     *
     * @param tp TabsPane object we want to register
     * @return TabsPane object, if registered successfully
     */
    public MyGuiDnDReady registerDnDReady(MyGuiDnDReady dndReady) {
        if (!dnds.contains(dndReady)) {
            // Adds dnd ready to manager list
            dnds.add(dndReady);
            // Adds mouse stuff to dnd ready
            dndReady.registerGuiManager(this);

            return dndReady;
        }
        return null;
    }

    /**
     * Finds out what (if any) DnDReady componet is located at position
     * specified by scrP x and y point in the screen.
     *
     * (Note: scrP x and y are coordinates in screen system, not any component
     *        local system).
     *
     * @param scrP Point in the screen
     * @return If there is any TabsPane component at specified location it will
     *         be returned as a result. Otherwise null value will be returned.
     */
    public MyGuiDnDReady getDnDReadyAtAccepting(Point scrP, Object o) {
        if (dnds.isEmpty())
            return null;

        MyGuiDnDReady found = null;
        
        for(MyGuiDnDReady dndReady : dnds)
            if ( ((JComponent)dndReady).isShowing() ) {
                Point localPoint = MyGuiHelper.toLocalSystem(dndReady, scrP);

                if (dndReady.accepts(o) && dndReady.isValidDropLocation(localPoint) &&
                    MyGuiHelper.compareCompsByZIndex((Component)dndReady, (Component)found)>=0)
                {
                   found = dndReady;
                }
            }

        return found;
    }

    
    ////////////////////////////////////////////////////////////////////////////
    //                  M O U S E   L I S T E N E R S   S T U F F             //
    ////////////////////////////////////////////////////////////////////////////

    public void mouseClicked(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {
        Object source = e.getSource();

        if (source instanceof MyGuiDnDReady) {
            MyGuiDnDReady dndReady = (MyGuiDnDReady) source;
            
            Point sourceLocalPoint = MyGuiHelper.toLocalSystem(dndReady, e.getLocationOnScreen());

            objToDrag = dndReady.getObjectToPickAt(sourceLocalPoint);

            if (objToDrag!=null) {
                setMode(Mode.PrepareForDragTab);
                srcDnd = dndReady;
                pickUpScrPoint = e.getLocationOnScreen();
//                System.out.println("MyGuiManager.mousePressed(), picked up obj: "+objToDrag);
                setGlassPaneVisibleOverComp(true, (Component) e.getSource());
            }
        }
    }
    public void mouseReleased(MouseEvent e) {

        switch (mode) {
            
            case PrepareForDragTab : /* Do nothing */ break;

            case DragObj :
                MyGuiDnDReady dstDnd = getDnDReadyAtAccepting(e.getLocationOnScreen(), objToDrag);
                
                if (dstDnd != null) {
                    JComponent dragParent = (JComponent) MyGuiHelper.getFirstAncestorAfterJScrollPane((Component) objToDrag);
                    
                    TabsPane tabsPane = null;

                    if (dragParent instanceof TabsPane)
                    {
                        
                        tabsPane = (TabsPane) dragParent;

                        tabsPane.remove((Component)objToDrag);

                        // 2'nd put dragged object to found accepting dnd ready
                        Point localDstPoint = e.getLocationOnScreen();
                        SwingUtilities.convertPointFromScreen(localDstPoint, (Component)dstDnd);

                        boolean dropWentOk = dstDnd.dropObjectAt(objToDrag, localDstPoint);

                        // 3'rd if drop unssuceed put dragged object back to it's parent
                        if ( !dropWentOk )
                            srcDnd.dropObjectAt(objToDrag, pickUpScrPoint);


                        // 4'th if source tabs pane is left empty, remove it
                        if (dragParent.getParent() instanceof MultiSplitPaneCell &&
                            tabsPane.isEmpty() && !tabsPane.isStayAliveWhenEmpty())
                        {
                            MultiSplitPaneCell cell = (MultiSplitPaneCell) dragParent.getParent();
                            MultiSplitPane msp = (MultiSplitPane) cell.getParent();

                            msp.removeCellWithComp(tabsPane);
                            msp.componentResized(null);
                        }

                        // Clear gui manager state and hide glass pane
                        setMode(Mode.None);
                        glass.drawables.clear();
                        setGlassPaneVisibleOverComp(false, (Component) e.getSource());

                        getRootMSP().cleanUpRedundance();
                    }
                }
                break;
        }
    }
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mouseDragged(MouseEvent e) {
        
        switch (mode) {
            
            case PrepareForDragTab :
                if (MyGuiHelper.getDistance(pickUpScrPoint, e.getLocationOnScreen()) > distanceToDrag)
                    setMode(Mode.DragObj.DragObj);
                break;
                
            case DragObj :
                MyGuiDnDReady dnDReady = getDnDReadyAtAccepting(e.getLocationOnScreen(), objToDrag);
//                System.out.println("dragged, dndReady: "+dnDReady);
                glass.drawables.clear();
                if (dnDReady!=null && ((Component)dnDReady).isShowing())
                    glass.drawables.add(dnDReady.getFutureLayoutDrawable(MyGuiHelper.toLocalSystem(dnDReady, e.getLocationOnScreen())));
                glass.repaint();
                break;
        }
    }
    public void mouseMoved(MouseEvent e) {}
    

////////////////////////////////////////////////////////////////////////////////
//                            G L A S S   P A N E                             //
////////////////////////////////////////////////////////////////////////////////

    protected void setGlassPaneVisibleOverComp(boolean visible, Component c) {
        if (glass==null) {
            glass = new GlassPane();
            JRootPane root = SwingUtilities.getRootPane(c);
            root.setGlassPane(glass);
        }
        glass.setVisible(visible);
    }
    
    private class GlassPane extends JComponent {

        // List of rectangles to draw (in screen coordinates system)
        public ArrayList<MyGuiDnDDrawable> drawables = new ArrayList<MyGuiDnDDrawable>();

        @Override
        public void paintComponent(Graphics g) {
            for(MyGuiDnDDrawable drawable : drawables)
                drawable.paintDrawabe(g, this);
        }
    }
}